// 盲测调试模式
const DEBUG_REPORT = CC_DEBUG;
// 控制上报日志的输出
const REPORT_LOG = true;
// 巨量上报地址
const REPORT_URL = "https://analytics.oceanengine.com/api/v2/conversion";
// openid查询数据服务器地址
const DataDomain = CC_DEBUG ? "http://20.0.0.58:20003/" : "https://apidemo6.ckbuyu.com/";

// 上报激活窗口期
const ACTIVE_WINDOW = 180 * 24 * 60 * 60;
// 一天毫秒数
const DAY_MS = 24 * 60 * 60 * 1000;
// 当前项目包名标识
const AppPackage = "H5JustGoGoGoTT";
// 游戏APPID
const GameAppID = "tte291942c7d9f48b502";

// 客户端巨量上报、及抖音数据查询上报
export default class TTReportAgent {
    // 单例实例
    private static _instance: TTReportAgent;

    // openid
    private _openId = "";
    // 点击id
    private _clickID = "";
    // ipu(广告累计点击次数)阈值，低于该值则不上报有效转化事件，默认值为5
    private _ipuThreshold = 5;
    // ecpm阈值，低于该值则不上报有效转化事件，默认值为150
    private _ecpmThreshold = 150;
    // ecpm request timer;
    private _timeID = 0;

    // 私有构造函数，防止外部实例化
    private constructor() {
        this.getRegisterTime();
        (window as any).tt?.setStorageSync("loginTime", Date.now());
        this.queryServerAdConfig();
    }

    /** 单例 */
    public static getIns(): TTReportAgent {
        if (!TTReportAgent._instance) {
            TTReportAgent._instance = new TTReportAgent();
        }
        return TTReportAgent._instance;
    }

    private static prefix = "#TTReportAgent";
    // 普通日志
    public static log(...args: any[]) {
        if (REPORT_LOG) {
            console.log(this.prefix, ...args);
        }
    }
    // 带方法名的日志
    public static methodLog(methodName: string, ...args: any[]) {
        if (REPORT_LOG) {
            console.log(`${this.prefix} [${methodName}]`, ...args);
        }
    }
    // 警告日志
    public static warn(...args: any[]) {
        console.warn(this.prefix, ...args);
    }
    // 带方法名的警告日志
    public static methodWarn(methodName: string, ...args: any[]) {
        console.warn(`${this.prefix} [${methodName}]`, ...args);
    }

    // 注册时间
    private getRegisterTime(): number {
        let registerTime = (window as any).tt?.getStorageSync("registerTime");
        if (!registerTime) {
            registerTime = Date.now();
            (window as any).tt?.setStorageSync("registerTime", registerTime);
        }
        return registerTime;
    }
    // 本次登陆时间
    private getLoginTime(): number {
        let loginTime = (window as any).tt?.getStorageSync("loginTime");
        if (!loginTime) {
            loginTime = Date.now();
        }
        return loginTime;
    }

    // 网络请求
    private async XMLHttpRequest(url: string, method = "GET", data = {}) {
        const timeout = 15000;
        TTReportAgent.log("XMLHttpRequest", url);
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            let jsonData = "";
            xhr.timeout = timeout;
            if (method === "POST") {
                jsonData = JSON.stringify(data);
                xhr.open('POST', url, true);
                xhr.setRequestHeader("Content-type", "application/json");
            }
            else {
                xhr.open('GET', url, true);
            }
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        try {
                            const date = JSON.parse(xhr.responseText);
                            TTReportAgent.log("XMLHttpRequest", date);
                            resolve(date);
                        } catch (error) {
                            TTReportAgent.warn('Failed to parse JSON response:', error, ' Response text:', xhr.responseText);
                            resolve({ rawResponse: xhr.responseText, parseError: error.message });
                        }
                    } else {
                        TTReportAgent.warn(`HTTP request failed with status: ${xhr.status}, Response: ${xhr.responseText}`);
                        const errorMessage = `HTTP request failed with status: ${xhr.status}, Response: ${xhr.responseText}`;
                        if (xhr.status >= 400 && xhr.status < 500) {
                            resolve({ error: errorMessage, status: xhr.status, response: xhr.responseText });
                        } else {
                            reject(new Error(errorMessage));
                        }
                    }
                }
            };
            xhr.onerror = function () {
                TTReportAgent.warn('Network request failed, url:', url);
                reject(new Error('Network request failed'));
            };
            xhr.ontimeout = function () {
                TTReportAgent.warn('Network request timeout after', timeout, 'ms, url:', url);
                reject(new Error(`Network request timeout after ${timeout}ms`));
            };
            if (method === "POST") {
                xhr.send(jsonData);
            }
            else {
                xhr.send();
            }
        });
    }

    /** 获取openid信息 */
    public async getOpenId(code: string) {
        try {
            if (!code || typeof code !== 'string') {
                TTReportAgent.warn('Invalid code parameter for getOpenId');
                return "";
            }
            const encodedCode = encodeURIComponent(code);
            const url = `${DataDomain}getDouyinOpenID?code=${encodedCode}&package=${AppPackage}`;
            TTReportAgent.log('Requesting openid with URL:', url);
            const data = await this.XMLHttpRequest(url) as any;
            if (!data || !data.openid) {
                TTReportAgent.warn('Failed to getTTOpenID, no valid openid returned. AppPackage=', AppPackage);
                TTReportAgent.log('getTTOpenID response:', data);
                return "";
            }
            TTReportAgent.log('getTTOpenID success:', data.openid);
            this._openId = data.openid as string;
            this.reportUserInfo();
            return this._openId;
        } catch (error) {
            TTReportAgent.warn('Failed to getTTOpenID:', error);
            return "";
        }
    }

    // 获取clickid
    private getClickId(enableSaved: boolean): string {
        if (DEBUG_REPORT) {
            TTReportAgent.log("DEBUG_REPORT getClickId");
            this._clickID = "debug_clickid_just_for_local_test";
            return this._clickID;
        }
        if (this._clickID && enableSaved) {
            return this._clickID;
        }

        const options = (window as any).tt?.getLaunchOptionsSync();
        let optionclickid = options?.query?.clickid;
        if (!optionclickid) {
            optionclickid = options?.query?.ad_params?.clickid;
        }

        if (optionclickid) {
            this._clickID = optionclickid || "";
            (window as any).tt?.setStorageSync("clickid", this._clickID);
            TTReportAgent.log("set clickid in storage", this._clickID);
            return this._clickID;
        }
        else if (enableSaved) {
            this._clickID = (window as any).tt?.getStorageSync("clickid") || "";
            TTReportAgent.log("no clickid in query, get clickid from storage", this._clickID);
            return this._clickID;
        } else {
            TTReportAgent.log("no clickid in query");
            return "";
        }
    }

    // 判断是否处于有效上报激活、注册时间内
    private isActiveTimeValid(): boolean {
        if (DEBUG_REPORT) {
            TTReportAgent.log("DEBUG_REPORT isActiveTimeValid");
            return true;
        }
        const registerTime = this.getRegisterTime();
        const loginTime = this.getLoginTime();
        const gapSce = Math.floor((loginTime - registerTime) / 1000);
        if (gapSce > 15 && gapSce <= ACTIVE_WINDOW) {
            TTReportAgent.methodLog("onTTReportActive", "can not report active again in 180 days!");
            return false;
        }
        return true;
    }

    /**
     * 抖音小游戏 激活
     */
    public async onTTReportActive() {
        const clickid = this.getClickId(false);
        if (!clickid) return;
        if (!this.isActiveTimeValid()) return;

        const data = {
            "event_type": "active",
            "context": {
                "ad": {
                    "callback": this._clickID
                }
            },
            "timestamp": Date.now()
        };
        const responseData = await this.XMLHttpRequest(REPORT_URL, "POST", data);
        TTReportAgent.methodLog("onTTReportActive", "responsedata= ", responseData);
    }

    /**
     * 抖音小游戏 注册
     */
    public async onTTReportRegister() {
        const clickid = this.getClickId(false);
        if (!clickid) return;
        if (!this.isActiveTimeValid()) return;

        const data = {
            "event_type": "active_register",
            "context": {
                "ad": {
                    "callback": this._clickID
                }
            },
            "timestamp": Date.now()
        };
        const responseData = await this.XMLHttpRequest(REPORT_URL, "POST", data);
        TTReportAgent.methodLog("onTTReportRegister", "responsedata= ", responseData);
    }

    /**
     * 抖音小游戏 广告变现ROI
     */
    public async onTTROIToTAQ() {
        const clickid = this.getClickId(true);
        if (!clickid) return;

        let sysinfo = (window as any).tt?.getSystemInfoSync();
        let data = {
            "event_type": "lt_roi",
            "context": {
                "ad": {
                    "callback": this._clickID,
                    "match_type": 2
                },
                "device": {
                    "platform": sysinfo?.platform
                }
            },
            "timestamp": Date.now()
        };
        const responseData = await this.XMLHttpRequest(REPORT_URL, "POST", data);
        TTReportAgent.methodLog("onTTROIToTAQ", "response:", responseData);
    };

    // 计算注册后的天数
    private calcDaysFromReg(registerTime: number, loginTime: number) {
        const firstLoginDate = new Date(registerTime);
        const activeDate = new Date(loginTime);
        firstLoginDate.setHours(0, 0, 0, 0);
        activeDate.setHours(0, 0, 0, 0);
        const timeDifference = activeDate.getTime() - firstLoginDate.getTime();
        const daysFromFirstLogin = Math.floor(timeDifference / DAY_MS) + 1;
        return daysFromFirstLogin;
    };

    /** 抖音小游戏 留存 */
    public async onLiuCunToTAQ() {
        const clickid = this.getClickId(true);
        if (!clickid) return;

        const registerTime = this.getRegisterTime();
        const loginTime = this.getLoginTime();
        const daysFromFirstLogin = this.calcDaysFromReg(registerTime, loginTime);
        TTReportAgent.log("当前是首次登录后的第" + daysFromFirstLogin + "天");
        if (daysFromFirstLogin < 2 || daysFromFirstLogin > 7) {
            return;
        }

        let eventname = "";
        switch (daysFromFirstLogin) {
            case 2:
                eventname = "next_day_open";
                break;
            case 3:
                eventname = "retention_3d";
                break;
            case 4:
                eventname = "retention_4d";
                break;
            case 5:
                eventname = "retention_5d";
                break;
            case 6:
                eventname = "retention_6d";
                break;
            case 7:
                eventname = "retention_7d";
                break;
        }
        const data = {
            "event_type": eventname,
            "context": {
                "ad": {
                    "callback": this._clickID
                }
            }
        };
        let responseData = await this.XMLHttpRequest(REPORT_URL, "POST", data);
        TTReportAgent.methodLog("onLiuCunToTAQ", "responsedata= ", responseData);
    };

    /**
     * 查询实时广告数据
     */
    public reportValidConversion() {
        const clickid = this.getClickId(true);
        if (!clickid) return;

        if (this.queryIsReported() && !DEBUG_REPORT) {
            TTReportAgent.methodLog("reportValidConversion", "no need report conversion again, because it has been reported before!");
            return;
        }

        clearTimeout(this._timeID);
        this.queryServerAdConfig();
        TTReportAgent.methodLog("reportValidConversion");
        this._timeID = window.setTimeout(() => {
            const openId = this._openId;
            TTReportAgent.methodLog("reportValidConversion", "start", openId, " adConfig=", this._ipuThreshold, this._ecpmThreshold);

            // this.XMLHttpRequest(QUERY_ECPM_URL, 'POST', {
            //     "open_id": openId,
            //     "date_hour": this.getFomratDate('yyyy-MM-dd HH')
            // }).then((data) => {
            //     TTReportAgent.methodLog("reportValidConversion", "success! response:", data);
            //     this._procECPM(data);
            // }, (error) => {
            //     TTReportAgent.methodLog("reportValidConversion", "faild! reason:", error);
            // });
            this.XMLHttpRequest(DataDomain + "getEcpmData", 'POST', {
                "open_id": openId,
                "date_hour": this.getFomratDate('yyyy-MM-dd HH'),
                "game_appid": GameAppID,
                "app_package": AppPackage
            }).then((data) => {
                TTReportAgent.methodLog("reportValidConversion", "success! response:", data);
                this._procECPM(data);
            }, (error) => {
                TTReportAgent.methodLog("reportValidConversion", "faild! reason:", error);
            });
        }, 60 * 1000);
    };

    // 查询是否已上报有效转化事件
    private queryIsReported() {
        const isReported = (window as any).tt?.getStorageSync("isReported");
        return isReported == "true";
    }
    private saveIsReported() {
        if (DEBUG_REPORT) {
            TTReportAgent.log("DEBUG_REPORT saveIsReported");
            return;
        }
        (window as any).tt?.setStorageSync("isReported", "true");
    }

    // 处理ECPM数据，并上报"关键行为"
    private async _procECPM(jsonObj: any) {
        try {
            const records = jsonObj?.data?.records;
            if (records.length < this._ipuThreshold) {
                TTReportAgent.methodLog("_procECPM", "IPU not enough, not report conversion, cur ipu:", records.length, " target ipu:", this._ipuThreshold);
                return;
            }
            // 对records数组中cost字段的值从大到小进行排序，然后再计算0-this._ipuThreshold范围内的cost累加值
            records.sort((a, b) => b.cost - a.cost);
            const topRecords = records.slice(0, this._ipuThreshold);
            const totalCostTopN = topRecords.reduce((sum, item) => sum + item.cost, 0);

            // 判断是否满足上报条件，满足则上报有效转化事件
            const aveEcpm = totalCostTopN / 100000 * 1000 / (this._ipuThreshold || 1);
            if (aveEcpm >= this._ecpmThreshold) {
                let data = {
                    "event_type": "game_addiction",
                    "context": {
                        "ad": {
                            "callback": this._clickID,
                            "match_type": 3
                        }
                    },
                    "timestamp": Date.now()
                }
                const responseData = await this.XMLHttpRequest(REPORT_URL, "POST", data);
                TTReportAgent.methodLog("_procECPM", "response:", responseData);
                this.saveIsReported();
            }
            else {
                TTReportAgent.methodLog("_procECPM", "ECPM not enough, not report conversion, aveEcpm:", aveEcpm, " target ecpm:", this._ecpmThreshold);
            }
        } catch (error) {
            TTReportAgent.methodWarn("procECPM", "faild!", error);
        }
    };

    private getFomratDate(format = "yyyy-MM-dd HH:mm:ss") {
        const date = new Date();
        const year = date.getFullYear();
        const month = date.getMonth() + 1; // 月份是从 0 开始的，所以需要加 1
        const day = date.getDate();
        const hours = date.getHours();
        const minutes = date.getMinutes();
        const seconds = date.getSeconds();

        const formattedDate = format
            .replace("yyyy", `${year}`)
            .replace("MM", month < 10 ? `0${month}` : `${month}`)
            .replace("dd", day < 10 ? `0${day}` : `${day}`)
            .replace("HH", hours < 10 ? `0${hours}` : `${hours}`)
            .replace("mm", minutes < 10 ? `0${minutes}` : `${minutes}`)
            .replace("ss", seconds < 10 ? `0${seconds}` : `${seconds}`);

        return formattedDate;
    };

    /** 查询更新服务器最新的IPU和ECPM */
    private async queryServerAdConfig() {
        try {
            let t = Date.now();
            const data = await this.XMLHttpRequest(`${DataDomain}static/${AppPackage}/GameConfig.json?t=${t}`) as any;
            if (!data) {
                TTReportAgent.warn('Failed to queryServerAdConfig.');
                return;
            }
            TTReportAgent.log('queryServerAdConfig:', data);
            this._ipuThreshold = parseInt(data.ipu);
            this._ecpmThreshold = parseInt(data.ecpm);
        } catch (error) {
            TTReportAgent.warn('Failed to queryServerAdConfig:', error);
            return;
        }
    }

    // 位置查询
    private async getLocationByIP(): Promise<string> {
        try {
            const data = await this.XMLHttpRequest(DataDomain + "getIPLocation") as any;
            if (!data || data.msg != "SUCCESS") {
                TTReportAgent.warn('Failed to queryLocation.');
                return "";
            }
            const city = data.city || "";
            TTReportAgent.log('queryLocation:', city);
            return city;
        } catch (error) {
            TTReportAgent.warn('Failed to queryLocation:', error);
            return "";
        }
    }

    // 上报用户信息
    private async reportUserInfo() {
        try {
            const appid = GameAppID;
            const openId = this._openId;
            const options = (window as any).tt?.getSystemInfoSync();
            const fromstr = options?.appName || AppPackage;
            const model = options?.model || "";
            const city = await this.getLocationByIP();
            const datastr1 = "appId=" + appid + ",openId=" + openId + ",from=" + fromstr + ",model=" + model + ",city=" + city;
            TTReportAgent.log("上报游戏来源:", datastr1);
            this.XMLHttpRequest(DataDomain + "reportUserInfo", "POST", {
                datastr: datastr1
            });
        } catch (error) {
            TTReportAgent.warn('Failed to reportUserInfo:', error);
        }
    }

    // 抖音登陆，并获取相关信息
    public login(succFunc?: (res: any) => void, failFunc?: (res: any) => void) {
        if (!(window as any).tt) {
            TTReportAgent.warn('TT API not available, skipping login');
            setTimeout(() => {
                succFunc && succFunc({ code: 'mock_code_for_non_tt_environment' });
            }, 100);
            return;
        }

        this.onTTReportActive();
        TTReportAgent.log("执行抖音登陆！");
        (window as any).tt?.login({
            success: async (res) => {
                TTReportAgent.log(`tt login 调用成功，code: ${res.code}`);
                try {
                    const openId = await this.getOpenId(res.code);
                    res.openid = openId;
                    this.onTTReportRegister();
                    this.onLiuCunToTAQ();
                    DEBUG_REPORT && this.reportValidConversion();
                } catch (error) {
                    TTReportAgent.warn('Error in login success handler:', error);
                }
                succFunc && succFunc(res);
            },
            fail: (res) => {
                TTReportAgent.log("login fail", res);
                if (failFunc) {
                    failFunc(res);
                } else if (succFunc) {
                    TTReportAgent.warn('Login failed but calling success callback to keep game running');
                    succFunc({ code: 'mock_code_for_login_failure' });
                }
            },
            complete: () => {
                TTReportAgent.log("tt login 调用完成");
            }
        });
    }
}